package com.gc.gpsoo;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gc.gpsoo.beans.GpsooCarData;
import com.gc.gpsoo.beans.GpsooDevData;
import com.gc.gpsoo.beans.GpsooHistoryData;
import com.gc.gpsoo.exception.GpsooSDKException;
import com.gc.gpsoo.util.JsJavaJsonFormatter;
import com.gc.common.util.StandradMD5;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 封装的API
 * @author 章瑱彬 电话：15914921094 QQ：562800930
 * @since 2015年3月11日
 */
public class GpsooSDK {
	private static GpsooSDK gpsooSDK; 
	private String account;
	private String password;
	private String accessToken;
	private final String URL = "http://api.gpsoo.net/1";
	private final String MAPTYPE = "BAIDU";
	public static HashMap<Integer, String> ERRORMAP = new HashMap<Integer, String>();
	static {
		ERRORMAP.put(10001, "系统错误");
		ERRORMAP.put(10002, "请求的接口不存在");
		ERRORMAP.put(10003, "请求频率超过上限");
		ERRORMAP.put(10004, "access_token不存在");
		ERRORMAP.put(10005, "access_token错误");
		ERRORMAP.put(10006, "access_token已过期,请重新获取");		
		ERRORMAP.put(20001, "账号或密码错误");
		ERRORMAP.put(20002, "缺失必选参数(%s),请参考API文档");
		ERRORMAP.put(20003, "参数(%s)值非法，%s,请参考API文档");
		ERRORMAP.put(20004, "监控账号不存在");
		ERRORMAP.put(20005, "无权查看该账号(%s)信息");
		ERRORMAP.put(20006, "%s账号名下设备数量超过上限");
		ERRORMAP.put(20007, "IMEI不存在(%s)");
		ERRORMAP.put(20008, "无权查看该设备(%s)信息");
		ERRORMAP.put(20009, "请求的设备数量超过上限");
		ERRORMAP.put(20010, "该设备已过期(%s)");
		ERRORMAP.put(20011, "地图类型错误，请参考API文档");
		ERRORMAP.put(20012, "非法经纬度");
	}

	/**
	 * 更具账号密码初始化账号
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @throws GpsooSDKException
	 * @throws IOException
	 * @throws ClientProtocolException 
	 * @since 2015年3月11日
	 */
	public GpsooSDK(String account, String password) throws IOException, GpsooSDKException {
		this.account = account;
		this.password = password;
		loadAccesstoken();

	}


	public GpsooSDK() throws IOException, GpsooSDKException {
		this.account = GpsooSDK.getConfigAccount();
		this.password = GpsooSDK.getConfigPassword();
		loadAccesstoken();
	}

	public static GpsooSDK getInstance() throws IOException, GpsooSDKException {
		if (gpsooSDK == null) {
			gpsooSDK = new GpsooSDK(getConfigAccount(), getConfigPassword());
		}
		return gpsooSDK;
	}

	public static GpsooSDK getInstance(String account, String password) throws IOException, GpsooSDKException {
		if (gpsooSDK == null || !gpsooSDK.getAccount().equals(account)) {
			gpsooSDK = new GpsooSDK(account, password);
		}
		return gpsooSDK;
	}

	private static Properties configFile;

	private static String getConfigAccount() {
		return getProperties().getProperty("account");
	}

	private static String getConfigPassword() {
		return getProperties().getProperty("password");
	}

	private static Properties getProperties() {
		if (configFile == null) {
			try {
				configFile = new Properties();
				InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config/GpsooSDK.properties");
				configFile.load(is);
			} catch (IOException e) {
				e.printStackTrace();
				configFile = null;
			}

		}
		return configFile;
	}

	/**
	 * 通过HttpClient获取服务器数据，并封装成Map
	 *
	 * @author 章瑱彬
	 * 电话：15914921094
	 * QQ：562800930
	 * @since 2015年3月31日
	 */
	public JSONObject loadHttpClientResult(String urls) throws IOException, GpsooSDKException {
		if (accessToken == null && !urls.contains("/auth/access_token")) {
			loadAccesstoken();
		}
		urls += getCommonParam();
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpgets = new HttpGet(urls);
		httpgets.setHeader("Content-Type", "text/html; charset=UTF-8");
		HttpResponse httpResponse = httpClient.execute(httpgets);
		HttpEntity httpEntity = httpResponse.getEntity();
		if(httpEntity != null){
			String resultString = EntityUtils.toString(httpEntity, ContentType.getOrDefault(httpEntity).getCharset().toString());// 对返回的html代码进行解析转码
			resultString = JsJavaJsonFormatter.jsFormatterToJava(resultString);
			JSONObject resultJson = JSONObject.parseObject(resultString);

			if (!resultJson.get("ret").equals(0)) {

				if (resultJson.get("ret").equals(10006)) {
					loadAccesstoken();
					return loadHttpClientResult(urls);
				} else
					throw new GpsooSDKException(GpsooSDK.ERRORMAP.get(resultJson.get("ret")));
			}
			return resultJson;
		} else
			throw new GpsooSDKException("读取服务器数据错误！");
	}
	
	/**
	 * 获取accesstoken
	 * 
	 * @param account
	 *            经销商的账号
	 * @param time
	 *            UNIX时间戳
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @since 2015年3月11日
	 */
	public String loadAccesstoken(String account, String password,
								  Date time) throws IOException, GpsooSDKException {
		String signature;
		String methods = "/auth/access_token";
		/* 加密的签名，算法为:md5(md5(经销商账号的密码) + time) */
		Long ltime = time.getTime() / 1000;
		signature = StandradMD5.cell32(StandradMD5.cell32(password) + ltime);
		String params = "?account=" + account + "&time=" + ltime + "&signature=" + signature;
		JSONObject accesstokenMap = loadHttpClientResult(URL + methods + params);
		if (!accesstokenMap.get("ret").equals(0)) {
			throw new GpsooSDKException(GpsooSDK.ERRORMAP.get(accesstokenMap.get("ret")));
		}
		accessToken = (String) accesstokenMap.get("accessToken");

		return accessToken;
	}

	/**
	 * 
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @throws IOException 
	 * @throws ClientProtocolException 
	 * @since 2015年3月11日
	 */
	public String loadAccesstoken() throws IOException, GpsooSDKException {
		return loadAccesstoken(this.account, this.password, new Date());
	}

	public String getCommonParam() {
		return "&access_token=" + accessToken + "&account=" + account + "&time=" + (new Date().getTime() / 1000);
	}


	/**
	 * 获取一个账户名下所有设备最新位置信息，默认账号为当前登陆账号
	 *
	 * @return
	 * @author 章瑱彬
	 * 电话：15914921094
	 * QQ：562800930
	 * @since 2015年3月31日
	 */
	public List<GpsooCarData> loadMonitor() throws IOException, GpsooSDKException {
		return loadMonitor(this.account);
	}
	
	/**
	 * 获取一个账户名下所有设备最新位置信息
	 * 
	 * @param target
	 *            要监控的账户(在汽车在线平台的账号)，目标账号名下最多只能有100个设备
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @since 2015年3月11日
	 */
	public List<GpsooCarData> loadMonitor(String target) throws IOException, GpsooSDKException {
		String methods = "/account/monitor";
		String params = "?target=" + target + "&map_type=" + MAPTYPE;
		JSONObject resultJson = loadHttpClientResult(URL + methods + params);
		List<GpsooCarData> cars = JSON.parseArray(resultJson.getJSONArray("data").toJSONString(),GpsooCarData.class);
		return cars;
	}

	/**
	 * 获取单个/批量设备最新位置信息
	 * 
	 * @param imeis
	 *            设备imei号，多个中间用英文逗号隔开； 如果设备过多，建议采用POST方式(一次最多100个IMEI)
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @since 2015年3月11日
	 */
	public List<GpsooCarData> loadTracking(String imeis) throws IOException, GpsooSDKException {
		String methods = "/devices/tracking";
		String params = "?imeis=" + imeis + "&map_type=" + MAPTYPE;
		JSONObject resultJson = loadHttpClientResult(URL + methods + params);
		List<GpsooCarData> cars = JSON.parseArray(resultJson.getJSONArray("data").toJSONString(),GpsooCarData.class);
		return cars;
	}

	/**
	 * 获取设备历史轨迹位置信息
	 * 
	 * @param imei
	 *            设备imei(一次只能一个IMEI)
	 * @param beginTime
	 *            开始时间(UTC) 秒数
	 * @param endTime
	 *            结束时间(UTC) 秒数
	 * @param limit
	 *            每次请求数据数量(一次最大1000条)
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @since 2015年3月11日
	 */
	public List<GpsooHistoryData> loadHistory(String imei, Date beginTime,
											  Date endTime, int limit) throws IOException, GpsooSDKException {
		String methods = "/devices/history";
		String params = "?imei=" + imei + "&map_type=" + MAPTYPE + "&begin_time=" + (beginTime.getTime() / 1000)
				+ "&end_time=" + (endTime.getTime() / 1000) + "&limit=" + limit;
		JSONObject resultJson = loadHttpClientResult(URL + methods + params);
		List<GpsooHistoryData> historys = JSON.parseArray(resultJson.getJSONArray("data").toJSONString(),GpsooHistoryData.class);
		return historys;
	}

	/**
	 * 根据经纬度得到中文地址
	 * 
	 * @param lng
	 *            经度
	 * @param lat
	 *            纬度
	 * @return
	 * @author 章瑱彬 电话：15914921094 QQ：562800930
	 * @since 2015年3月11日
	 */
	public String loadAddress(String lng, String lat) throws IOException, GpsooSDKException {
		String methods = "/tool/address";
		String params = "?lng=" + lng + "&lat=" + lat + "&map_type=" + MAPTYPE;
		JSONObject resultJson = loadHttpClientResult(URL + methods + params);
		return (String) resultJson.get("address");
	}

	/**
	 * 查询账号下所有设备的相关信息
	 *
	 * @param target 要监控的账户(在汽车在线平台的账号)，目标账号名下最多只能有100个设备
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @throws GpsooSDKException
	 * @author 章瑱彬
	 * 电话：15914921094
	 * QQ：562800930
	 * @since 2015年4月1日
	 */
	public List<GpsooDevData> loadDevinfo(String target) throws IOException, GpsooSDKException {
		String methods = "/account/devinfo";
		String params = "?target=" + target;
		JSONObject resultJson = loadHttpClientResult(URL + methods + params);
		List<GpsooDevData> devs = JSON.parseArray(resultJson.getJSONArray("data").toJSONString(), GpsooDevData.class);
		return devs;
	}

	/**
	 * 查询当前账号下所有设备的相关信息
	 *
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @throws GpsooSDKException
	 * @author 章瑱彬
	 * 电话：15914921094
	 * QQ：562800930
	 * @since 2015年4月1日
	 */
	public List<GpsooDevData> loadDevinfo() throws IOException, GpsooSDKException {
		return loadDevinfo(account);
	}

	public final String getAccount() {
		return account;
	}

	public final void setAccount(String account) {
		this.account = account;
	}
	
	public static void main(String[] args) {
		try {
			GpsooSDK gpsooSDK = new GpsooSDK("13823862062","123456");
			gpsooSDK.loadAccesstoken();
			System.out.println("address = " + gpsooSDK.loadAddress("116.1405", "24.294033"));

		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (GpsooSDKException e) {
			e.printStackTrace();
		}
		
	}
}
